/*
** $Id: lfunc.c,v 2.45 2014/11/02 19:19:04 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/

#define lfunc_c
#define LUA_CORE

#include "lprefix.h"


#include <stddef.h>

#include "lua.h"

#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"



CClosure* luaF_newCclosure( lua_State* L, int n ) {
    GCObject* o = luaC_newobj( L, LUA_TCCL, sizeCclosure( n ) );
    CClosure* c = gco2ccl( o );
    c->nupvalues = cast_byte( n );
    return c;
}


LClosure* luaF_newLclosure( lua_State* L, int n ) {
    GCObject* o = luaC_newobj( L, LUA_TLCL, sizeLclosure( n ) );
    LClosure* c = gco2lcl( o );
    c->p = NULL;
    c->nupvalues = cast_byte( n );

    while ( n-- ) {
        c->upvals[n] = NULL;
    }

    return c;
}

/*
** fill a closure with new closed upvalues
*/
void luaF_initupvals( lua_State* L, LClosure* cl ) {
    int i;

    for ( i = 0; i < cl->nupvalues; i++ ) {
        UpVal* uv = luaM_new( L, UpVal );
        uv->refcount = 1;
        uv->v = &uv->u.value;  /* make it closed */
        setnilvalue( uv->v );
        cl->upvals[i] = uv;
    }
}


UpVal* luaF_findupval( lua_State* L, StkId level ) {
    UpVal** pp = &L->openupval;
    UpVal* p;
    UpVal* uv;
    lua_assert( isintwups( L ) || L->openupval == NULL );

    while ( *pp != NULL && ( p = *pp )->v >= level ) {
        lua_assert( upisopen( p ) );

        if ( p->v == level ) { /* found a corresponding upvalue? */
            return p;    /* return it */
        }

        pp = &p->u.open.next;
    }

    /* not found: create a new upvalue */
    uv = luaM_new( L, UpVal );
    uv->refcount = 0;
    uv->u.open.next = *pp;  /* link it to list of open upvalues */
    uv->u.open.touched = 1;
    *pp = uv;
    uv->v = level;  /* current value lives in the stack */

    if ( !isintwups( L ) ) { /* thread not in list of threads with upvalues? */
        L->twups = G( L )->twups; /* link it to the list */
        G( L )->twups = L;
    }

    return uv;
}


void luaF_close( lua_State* L, StkId level ) {
    UpVal* uv;

    while ( L->openupval != NULL && ( uv = L->openupval )->v >= level ) {
        lua_assert( upisopen( uv ) );
        L->openupval = uv->u.open.next;  /* remove from 'open' list */

        if ( uv->refcount == 0 ) { /* no references? */
            luaM_free( L, uv );    /* free upvalue */
        }
        else {
            setobj( L, &uv->u.value, uv->v ); /* move value to upvalue slot */
            uv->v = &uv->u.value;  /* now current value lives here */
            luaC_upvalbarrier( L, uv );
        }
    }
}


Proto* luaF_newproto( lua_State* L ) {
    GCObject* o = luaC_newobj( L, LUA_TPROTO, sizeof( Proto ) );
    Proto* f = gco2p( o );
    f->k = NULL;
    f->sizek = 0;
    f->p = NULL;
    f->sizep = 0;
    f->code = NULL;
    f->cache = NULL;
    f->sizecode = 0;
    f->lineinfo = NULL;
    f->sizelineinfo = 0;
    f->upvalues = NULL;
    f->sizeupvalues = 0;
    f->numparams = 0;
    f->is_vararg = 0;
    f->maxstacksize = 0;
    f->locvars = NULL;
    f->sizelocvars = 0;
    f->linedefined = 0;
    f->lastlinedefined = 0;
    f->source = NULL;
    return f;
}


void luaF_freeproto( lua_State* L, Proto* f ) {
    luaM_freearray( L, f->code, f->sizecode );
    luaM_freearray( L, f->p, f->sizep );
    luaM_freearray( L, f->k, f->sizek );
    luaM_freearray( L, f->lineinfo, f->sizelineinfo );
    luaM_freearray( L, f->locvars, f->sizelocvars );
    luaM_freearray( L, f->upvalues, f->sizeupvalues );
    luaM_free( L, f );
}


/*
** Look for n-th local variable at line 'line' in function 'func'.
** Returns NULL if not found.
*/
const char* luaF_getlocalname( const Proto* f, int local_number, int pc ) {
    int i;

    for ( i = 0; i < f->sizelocvars && f->locvars[i].startpc <= pc; i++ ) {
        if ( pc < f->locvars[i].endpc ) { /* is variable active? */
            local_number--;

            if ( local_number == 0 ) {
                return getstr( f->locvars[i].varname );
            }
        }
    }

    return NULL;  /* not found */
}

